package ru.spb.osll.myfit.fixtures.base;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import ru.spb.osll.myfit.util.CollectionUtils;
import ru.spb.osll.myfit.util.ReflectionUtils;
import ru.spb.osll.myfit.util.StringUtils;
import util.GracefulNamer;
import fit.Parse;

public class OneLineFixtureUtils {
	public static List<String> getOneLineTable(Parse table, String[] args) {
		LinkedList<String> list = new LinkedList<String>();
		list.add(table.at(0, 1).at(0, 0).body);
		for (String arg : args) {
			list.add(arg);
		}
		return list;
	}

	public static String getMethodName(List<String> oneLineTable) {
		List<String> even = CollectionUtils.filterEven(oneLineTable);
		LinkedList<String> disgracedEven = new LinkedList<String>();
		for(String elem : even) {
			disgracedEven.add(GracefulNamer.disgrace(elem));
		}
		return StringUtils.join(disgracedEven, "");
	}

	public static Method getMethod(Class<?> clazz, String methodName)
			throws OneLineFixtureException {
		for (Method method : clazz.getMethods()) {
			if (method.getName().equalsIgnoreCase(methodName)) {
				return method;
			}
		}

		throw new OneLineFixtureException("Method " + methodName
				+ " not found in class " + clazz.getName());
	}

	public static FixtureMethodResult invokeMethod(Object instance,
			Method method, List<String> paramValues) throws Exception {
		Class<?> paramTypes[] = method.getParameterTypes();

		if (paramTypes.length != paramValues.size()) {
			throw new OneLineFixtureException(
					"Wrong number of parameters for method " + method.getName()
							+ " expected " + paramTypes.length + " but "
							+ paramValues.size() + " provided");
		}

		Object params[] = new Object[paramTypes.length];
		Iterator<String> paramValuesIter = paramValues.iterator();

		for (int i = 0; i < params.length; i++) {
			String paramValue = paramValuesIter.next();
			params[i] = createParam(paramTypes[i], paramValue);
			if (params[i] == null) {
				throw new OneLineFixtureException(
						"Error creating parameter of type "
								+ paramTypes[i].getName() + " from value "
								+ paramValue);
			}
		}

		Object res = method.invoke(instance, params);
		if (res instanceof Boolean) {
			boolean ok = (Boolean) res;
			if (ok) {
				return FixtureMethodResult.OK;
			} else {
				return FixtureMethodResult.ERROR;
			}
		}

		return FixtureMethodResult.NONE;
	}

	public static Object createParam(Class<?> type, String value) {
		Object res = null;
		Method valueOf;
		Class<?> realParamType = type;

		if (type.isPrimitive()) {
			realParamType = ReflectionUtils.primitiveTypeWrappersMap.get(type);
		}

		if (res == null) {
			try {
				valueOf = realParamType.getMethod("valueOf", String.class);
				if (Modifier.isStatic(valueOf.getModifiers())) {
					res = valueOf.invoke(null, value);
				}
			} catch (Exception e) {
			}
		}

		if (res == null) {
			try {
				Constructor<?> ctor = realParamType
						.getConstructor(String.class);
				res = ctor.newInstance(value);
			} catch (Exception e) {
			}
		}

		return res;
	}

	private OneLineFixtureUtils() {

	}
}
